#define FUSE_USE_VERSION  26
 
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#include <iostream>
#include <string>

#include "volume.local.h"
#include "volume.merger.h"

using namespace jolfs;

LocalVolume*  volume_a;
LocalVolume*  volume_b;
LocalVolume*  volume_c;
LocalVolume*  volume_d;
MergerVolume* volume_abcd;
MergerVolume* volume_bcd;
MergerVolume* volume_cd;

static int jolfs_getattr( const char* path, struct stat* stbuf )
{
  Entry* entry = volume_abcd->getEntry( path );
  
  memset( stbuf, 0, sizeof(struct stat) );
  
  if ( strcmp(path, "/") == 0 )
  {
    stbuf->st_mode = S_IFDIR | 0755;
    stbuf->st_nlink = 2; // '.' + '..' + contained directories
    stbuf->st_size  = 0;
  }
  else if ( entry != 0 )
  {
    switch (entry->getType())
    {
      case Entry::DIRECTORY:
        stbuf->st_mode = S_IFDIR | 0755; //TODO: fix file files access rights
        stbuf->st_nlink = 2; // '.' + '..' + contained directories
        stbuf->st_size  = entry->getSize();
        break;
      case Entry::FILE:
        stbuf->st_mode = S_IFREG | 0444; //TODO: fix file access rights
        stbuf->st_nlink = 1;
        stbuf->st_size  = entry->getSize();
        break;
    }
  }
  else
    return -ENOENT;

  return 0;
}

static int jolfs_readdir( const char* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi )
{
    (void) offset;
    (void) fi;

    // awful hack, volumes should be treated as virtual directories entries, but that's not
    // the case atm
    if ( strcmp(path,"/")==0 )
    {
      filler( buf, ".",  0, 0 );
      filler( buf, "..", 0, 0 );
      EntryList entries = volume_abcd->getEntries();
      EntryList::const_iterator it = entries.begin();
      for (; it != entries.end(); ++it )
        filler( buf, (*it)->getName().c_str(), 0, 0 );

      return 0;
    }

    Entry* entry = volume_abcd->getEntry( path );
    
    if( entry == 0 )
        return -ENOENT;

    filler( buf, ".",  0, 0 );
    filler( buf, "..", 0, 0 );
    
    EntryList entries = entry->getChildren();
    EntryList::const_iterator it = entries.begin();
    for (; it != entries.end(); ++it )
      filler( buf, (*it)->getName().c_str(), 0, 0 );

    return 0;
}

static int jolfs_open( const char* path, struct fuse_file_info* fi )
{
  Entry* entry = volume_abcd->getEntry( path );
  
  if ( entry == 0 )
    return -ENOENT;
  else if ( entry->getMode() & UMODE(1,0,0) == 0 )
    return -EACCES;
  else
    return 0;
}

static int jolfs_read( const char* path, char* buf, size_t size, off_t offset, struct fuse_file_info* )
{
    Entry* entry = volume_abcd->getEntry( path );

    if ( entry==0 )
      return -ENOENT;

    // Stupid dummy, dunno how to handle read atm, io manager not currently implemented
    return -ENOENT;
}

static struct fuse_operations jolfs_oper ;

int main( int argc, char** argv )
{  
  jolfs_oper.getattr = jolfs_getattr;
  jolfs_oper.readdir = jolfs_readdir;
  jolfs_oper.open    = jolfs_open;
  jolfs_oper.read    = jolfs_read;
  
  // + abcd/       ( merger )
  //   + a/        ( local  )
  //   + bcd/      ( merger )
  //     + b/      ( local  )
  //     + cd/     ( merger )
  //       + c/    ( local  )
  //       + d/    ( local  )
  
  volume_a    = new LocalVolume ( "a", "/tmp/jolfs/a" );
  volume_b    = new LocalVolume ( "b", "/tmp/jolfs/b" );
  volume_c    = new LocalVolume ( "c", "/tmp/jolfs/c" );
  volume_d    = new LocalVolume ( "d", "/tmp/jolfs/d" );
  volume_abcd = new MergerVolume( "abcd" );
  volume_bcd  = new MergerVolume( "bcd"  );
  volume_cd   = new MergerVolume( "cd"   );
  
  volume_abcd->addMergedVolume( volume_a   );
  volume_abcd->addMergedVolume( volume_bcd );
  
  volume_bcd->addMergedVolume( volume_b  );
  volume_bcd->addMergedVolume( volume_cd );
  
  volume_cd->addMergedVolume( volume_c );
  volume_cd->addMergedVolume( volume_d );
  
  return fuse_main(argc, argv, &jolfs_oper, NULL);
}